﻿/*
 * GNU GENERAL PUBLIC LICENSE v2
 * 
 * Developers: Alan Audette, Oguz Bastemur
 */
using System;
using System.Diagnostics;
using System.IO;
using SqlDatabaseBackup.Helper;

namespace SqlDatabaseBackup.Tools
{
    internal static class Backup
    {
        /// <summary>
        /// Handles the database backup processing
        /// </summary>
        /// <param name="server"></param>
        internal static bool ProcessBackup()
        {
            // Create a connection to the configured server
            Microsoft.SqlServer.Management.Smo.Server server = Backup.GetServer();

            // Don't execute if the server cannot be found
            if (server == null)
            {
                ToConsole.RedText("SQL Server doesn't exist! Please check the configuration file");
                return false;
            }

            // Confirm the connection with the user
            ToConsole.WriteLine("SQL SERVER : " + server.Name + " " + server.Information.Version);
            ToConsole.WriteLine();//NewLine 

            try
            {
                // Get the destination folder from the configuration
                string BackupFolder = Program.configuration.Backup.Path;

                if (!Directory.Exists(BackupFolder))
                {
                    if (Program.configuration.Backup.CreateNotExist)
                    {
                        if (!FileSystemHelper.CreateFolders(BackupFolder))
                            return false;
                    }
                }

                // Backup all the configured databases
                for (int y = 0; y < Program.configuration.Databases.Count; y++)
                {
                    // Create a shortcut to the database name
                    string DatabaseName = Program.configuration.Databases[y].Name;

                    // Make sure the database exists on the server
                    if (server.Databases[DatabaseName] != null)
                    {
                        // Format the complete path to the backup file by concatenating the folder,
                        // the database name, the date and file extension
                        string BackupFile = String.Format("{0}{1}_{2}.bak", BackupFolder, DatabaseName, DateTime.Now.ToString("yyyyMMdd hhmmss"));

                        // Execute the database backup
                        if (!BackupDatabase(server, DatabaseName, BackupFile))
                            return false;

                        // Add the path to the .bak file to the list of file outputs
                        Program.OutputFiles.Add(BackupFile);
                    }
                }
            }
            catch (Exception ex)
            {
                Log.Message("Error processing backup, " + ex.Message + "\n\r", EventLogEntryType.Error);
                Program.processFailed = true;
                return false;
            }

            // Notify the user that all the database backups are complete
            Log.Message("Backup process completed successfully", EventLogEntryType.SuccessAudit);
            return true;
        }

        /// <summary>
        /// Deletes files older than X days defined on configuration file
        /// </summary>
        internal static void DeleteFiles()
        {
            if (Program.configuration.Archive.DeleteFilesOlderThan <= 0)
                return;

            string BackupFolder = Program.configuration.Backup.Path;
            int days = Program.configuration.Archive.DeleteFilesOlderThan;

            Log.Message("Deleting old backups..");
            string [] files = Directory.GetFiles(BackupFolder);
            foreach (string file in files)
            {
                if (File.GetCreationTime(file).AddDays(days) < DateTime.Now)
                {
                    try
                    {
                        File.Delete(file);
                        Log.Message("File :" + file + " DELETED");
                    }
                    catch(Exception e)
                    {
                        Log.Message("File :" + file + " CAN NOT DELETED. Reason: "+ e.Message, EventLogEntryType.Error);
                    }
                }
            }
        }



        /// <summary>
        /// Establishes a connection to the configured Sql Server
        /// </summary>
        /// <returns>Sql SMO Server object with an established connection</returns>
        internal static Microsoft.SqlServer.Management.Smo.Server GetServer()
        {
            Microsoft.SqlServer.Management.Smo.Server server = null;

            try
            {
                // Open the connection to the server
                server = new Microsoft.SqlServer.Management.Smo.Server(Program.configuration.Server.Name);
            }
            catch (Exception ex)
            {
                Log.Message("Error connecting to server, " + ex.Message, EventLogEntryType.Error);
                Program.processFailed = true;
            }

            // Return the server
            return server;
        }

        /// <summary>
        /// Executes the backup of a database
        /// </summary>
        /// <param name="server">Sql Server to perform that backup on</param>
        /// <param name="databaseName">Name of the database on the server to backup</param>
        /// <param name="backupPath">Complete path to save the backup to</param>
        static bool BackupDatabase(Microsoft.SqlServer.Management.Smo.Server server, string databaseName, string backupPath)
        {
            try
            {
                // Alert the user that the database is being backed up
                Log.Message("Backing up database " + databaseName + " to " + backupPath + "...", EventLogEntryType.Information);

                // Create the Backup SMO object to manage the execution
                Microsoft.SqlServer.Management.Smo.Backup backup = new Microsoft.SqlServer.Management.Smo.Backup();


                // Add the file to backup to
                backup.Devices.Add(new Microsoft.SqlServer.Management.Smo.BackupDeviceItem(backupPath, Microsoft.SqlServer.Management.Smo.DeviceType.File));

                // Set the name of the database to backup
                backup.Database = databaseName;

                // Tell SMO that we are backing up a database
                backup.Action = Microsoft.SqlServer.Management.Smo.BackupActionType.Database;

                // Initialize the backup
                backup.Initialize = true;

                // Let SMO know that we want a notification upon completion
                backup.PercentCompleteNotification = 100;

                // Wire up an event handler for the completion of the backup process
                backup.PercentComplete += new Microsoft.SqlServer.Management.Smo.PercentCompleteEventHandler(backup_PercentComplete);

                // Begin execution of the backup
                backup.SqlBackup(server);
            }
            catch (Exception ex)
            {
                Log.Message("Error backing up database, " + ex.Message, EventLogEntryType.Error);
                Program.processFailed = true;
                return false;
            }

            return true;
        }

        /// <summary>
        /// Handles notification from SMO that the database backup is complete
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        static void backup_PercentComplete(object sender, Microsoft.SqlServer.Management.Smo.PercentCompleteEventArgs e)
        {
            // Alert the user that database backup is complete
            Console.WriteLine(" complete");
        }

        /// <summary>
        /// Archives the output files into a .zip file
        /// </summary>
        internal static void ArchiveFiles()
        {
            if (Program.configuration.Archive.ArchiveFiles)
            {
                ToConsole.WriteLine();

                // Alert the user that the archiving process has started
                Log.Message("Starting the archive process...", EventLogEntryType.Information);

                try
                {
                    // Get the destination folder from the configuration
                    string BackupFolder = Program.configuration.Backup.Path;

                    // Create the path to the archive file
                    string ArchivePath = BackupFolder + String.Format(Program.configuration.Archive.Filename, DateTime.Now.ToString("yyyyMMdd_hhmmss"));

                    // Generate the archive
                    ArchiveManager.ArchiveFiles(ArchivePath, Program.OutputFiles);

                    // Alert the user that archiving is complete
                    Log.Message("Archive process completed successfully", EventLogEntryType.SuccessAudit);
                }
                catch (Exception ex)
                {
                    Log.Message("Error archiving files, " + ex.Message, EventLogEntryType.Error);
                    Program.processFailed = true;
                    return;
                }

                try
                {
                    // Delete the original files, since they are now duplicated in the archive
                    foreach (string s in Program.OutputFiles)
                    {
                        if (File.Exists(s))
                            File.Delete(s);
                    }
                }
                catch (Exception ex)
                {
                    Log.Message("Error deleting files, " + ex.Message, EventLogEntryType.Error);
                    Program.processFailed = true;
                }
            }
        }
    }
}